use rtt_kernel_cpu::context::rt_hw_stack_init;
use rtt_kernel_mutex::KMutex;

use crate::alloc::boxed::Box;
use crate::alloc::string::String;
use crate::alloc::sync::Arc;
use crate::alloc::vec::Vec;
use crate::global::{CURRENT_THREAD, SCHEDULE_LIST, THREAD_DELETE_LIST};
use crate::scheduler::rt_schedule;
use crate::timer::Timer;
use core::ops::Deref;
use core::mem;

pub type Thread = Box<ObjThread>;
pub type StackPointer = Arc<KMutex<usize>>;
pub type Stack = Vec<u8>;

pub struct ObjThread {
    name: String,
    // 用Arc是因为创建虚假线程是要更改其数据
    sp: StackPointer,
    stack: Stack,
    // 优先级且支持优先级反转问题
    // 更改当前的优先级必须同步更改等待链表
    current_priority: usize,
    init_priority: usize,
}

impl ObjThread {
    #[inline]
    pub fn get_priority(&self) -> usize {
        self.current_priority
    }

    #[inline]
    pub fn get_name(&self) -> &str {
        self.name.as_ref()
    }

    #[inline]
    pub fn get_sp(&self) -> &StackPointer {
        &(self.sp)
    }

    #[inline]
    pub fn restore_priority(&mut self) {
        self.current_priority = self.init_priority
    }
}

pub fn rt_thread_weak(th: Thread) {
    let mut schedule_list = SCHEDULE_LIST.lock();
    schedule_list[th.get_priority()].push_front(th);
    rt_schedule()
}

// 取得被包装的线程的地址
pub fn _get_boxed_thread_id(th: &Thread) -> usize {
    (*th).deref() as *const _ as usize
}

pub fn rt_thread_remove(id_pri: (usize, usize)) -> Thread {
    let mut schedule_list = SCHEDULE_LIST.lock();
    let mut index = 0;
    let mut find = false;
    for i in schedule_list[id_pri.1].iter() {
        if _get_boxed_thread_id(i) == id_pri.0 {
            find = true;
            break;
        }
        index += 1;
    }
    if !find {
        panic!()
    }
    schedule_list[id_pri.1].remove(index)
}

pub fn rt_thread_update_pri(id_pri: (usize, usize), new_pri: usize) {
    let mut schedule_list = SCHEDULE_LIST.lock();
    let mut th = rt_thread_remove(id_pri);
    th.current_priority = new_pri;
    schedule_list[new_pri].push_back(th);
}

pub fn rt_thread_restore_pri(id_pri: (usize, usize)) {
    let mut schedule_list = SCHEDULE_LIST.lock();
    let mut th = rt_thread_remove(id_pri);
    th.restore_priority();
    schedule_list[th.current_priority].push_back(th);
}

// 空闲线程，用于清理
pub fn rt_thread_clean() {
    loop {
        // 析构已经结束的线程
        let mut list = THREAD_DELETE_LIST.lock();
        list.clear();
    }
}

pub extern "C" fn rt_thread_exit() {
    {
        let mut cur_th = CURRENT_THREAD.lock();
        let mut del_list = THREAD_DELETE_LIST.lock();
        // 取出当前线程，丢到回收站
        let dead_th = cur_th.thread_take_thread();
        del_list.push_back(dead_th);
    }
    // 开始调度
    rt_schedule()
}

pub extern "C" fn rt_thread_entry(param: *mut u32) {
    unsafe {
        let run = Box::from_raw(param as *mut Box<dyn FnOnce() + Send>);
        run();
    }
}

pub fn rt_thread_create(
    name: &str,
    _entry: Box<dyn FnOnce()>,
    _stack_size: usize,
    _priority: usize,
) -> Thread {
    let mut new_thread = ObjThread {
        name: name.into(),
        sp: Arc::new(KMutex::new(0)),
        stack: Vec::new(),
        current_priority: _priority,
        init_priority: _priority,
    };

    new_thread.stack.resize(_stack_size, 0);
    // new_thread.stack_address = &new_thread.stack[0] as *const u8 as _;

    let func = Box::new(_entry);
    let param = &*func as *const _ as *mut u32;

    {
        let mut sp_lock = new_thread.sp.lock();

        *sp_lock = rt_hw_stack_init(
            rt_thread_entry as u32,
            param as u32,
            &new_thread.stack[_stack_size - 4] as *const u8 as u32,
            rt_thread_exit as u32,
        ) as usize;
    }

    mem::forget(func);

    Box::new(new_thread)
}

pub fn rt_thread_startup(th: Thread) {
    rt_thread_weak(th);
}

pub fn rt_thread_sleep(ticks: usize) {
    let mut cur_th = CURRENT_THREAD.lock();
    let cur_th_raw = cur_th.get_thread_set_sp().unwrap();

    let timer = Timer::new(
        ticks,
        Box::new(move || {
            rt_thread_weak(cur_th_raw);
        }),
    );
    timer.start();

    rt_schedule()
}

pub fn rt_thread_yield() {
    let mut cur_th = CURRENT_THREAD.lock();
    let mut list = SCHEDULE_LIST.lock();

    let thread = cur_th.get_thread_set_sp().unwrap();
    list[thread.get_priority()].push_back(thread);

    rt_schedule()
}

pub struct MLibThread();

impl MLibThread {
    pub fn spawn<T>(name: &str, stack_size: usize, priority: usize, entry: T)
    where
        T: FnOnce() + 'static + Send,
    {
        MLibThread::_inner_spawn(name, Box::new(entry), stack_size, priority)
    }

    pub fn _inner_spawn(name: &str, entry: Box<dyn FnOnce()>, stack_size: usize, priority: usize) {
        let id = rt_thread_create(name, entry, stack_size, priority);
        rt_thread_startup(id);
    }

    pub fn sleep(ticks: usize) {
        rt_thread_sleep(ticks);
    }

    pub fn th_yield() {
        rt_thread_yield();
    }
}
